package cn.bonoon.handler.impl;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.util.Assert;

import cn.bonoon.kernel.exceptions.MethodErrorException;
import cn.bonoon.util.RSAManager;
import cn.bonoon.util.VerifyCodeHelper;

/**
 * <pre>
 * 登录时，对使用验证码的情况进行支持
 * 
 * 可以设置开启或不开启使用验证码的功能
 * 
 * </pre>
 * 使用默认的
 * {@link org.springframework.security.authentication.AuthenticationProvider AuthenticationProvider}
 * 为：
 * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider DaoAuthenticationProvider}
 * <p>
 * 记住我的功能需要实现
 * {@link org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices AbstractRememberMeServices}
 * 类来实现自动登录的功能
 * </p>
 * <pre>
 * 参照以下spring的类{@link org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter UsernamePasswordAuthenticationFilter}
 * </pre>
 * @author jackson
 * @see AbstractAuthenticationProcessingFilter
 */
public class AuthenticationExtendFilter extends AbstractAuthenticationProcessingFilter{
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
    // 是否开启验证码功能  
    private static final int validateCodeType = 1;  
	// 验证码字段  
    private String validateCodeParameter = "validateCode";  
    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    
    private boolean useVerifyCode = true;

    public AuthenticationExtendFilter() {
        super("/j_spring_security_check");
    }
    
    @Override  
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {  
        // 只接受POST方式传递的数据  
        if (!"POST".equals(request.getMethod())){  
            throw new MethodErrorException("不支持非POST方式的请求!");  
        }
        
        if(useVerifyCode){
	        // 开启验证码功能的情况  
	        VerifyCodeHelper.verify(request, validateCodeType, obtainValidateCodeParameter(request));
        }
        // 获取Username和Password  
        String username = obtainUsername(request);  
        String password = obtainPassword(request);  
  
        password = RSAManager.rsaManager.decryptJSString(password);
        // UsernamePasswordAuthenticationToken实现Authentication校验  
        AbstractAuthenticationToken authRequest = __authentication(request, username, password);  
  
        // 允许子类设置详细属性  
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        
        // 运行UserDetailsService的loadUserByUsername 再次封装Authentication  
        return this.getAuthenticationManager().authenticate(authRequest);
    }
    
    protected AbstractAuthenticationToken __authentication(HttpServletRequest request, String username, String password){

        // UsernamePasswordAuthenticationToken实现Authentication校验  
        return new UsernamePasswordAuthenticationToken(username, password);  
    }
    
    private String __checkString(String str){
    	return null == str ? "" : str.trim();
    }
  
    public String obtainValidateCodeParameter(HttpServletRequest request) {  
    	return __checkString(request.getParameter(validateCodeParameter));  
    }  
  
    protected String obtainUsername(HttpServletRequest request) {  
    	return __checkString(request.getParameter(usernameParameter)); 
    }  
  
    protected String obtainPassword(HttpServletRequest request) {  
    	return __checkString(request.getParameter(passwordParameter)); 
    }  
  
    public void setValidateCodeParameter(String validateCodeParameter) {  
        this.validateCodeParameter = validateCodeParameter;  
    }

	public void setUseVerifyCode(boolean useVerifyCode) {
		this.useVerifyCode = useVerifyCode;
	}  

    /**
     * Sets the parameter name which will be used to obtain the username from the login request.
     *
     * @param usernameParameter the parameter name. Defaults to "j_username".
     */
    public void setUsernameParameter(String usernameParameter) {
        Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
        this.usernameParameter = usernameParameter;
    }

    /**
     * Sets the parameter name which will be used to obtain the password from the login request..
     *
     * @param passwordParameter the parameter name. Defaults to "j_password".
     */
    public void setPasswordParameter(String passwordParameter) {
        Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
        this.passwordParameter = passwordParameter;
    }

    public final String getUsernameParameter() {
        return usernameParameter;
    }

    public final String getPasswordParameter() {
        return passwordParameter;
    }
}
